001 /* 002 * Copyright 2004 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.util; 020 021 import java.util.Map; 022 import java.beans.Introspector; 023 import java.beans.Expression; 024 import java.lang.reflect.InvocationHandler; 025 import java.lang.reflect.Method; 026 import java.lang.reflect.Proxy; 027 028 /** 029 * Invoication handler utility for a Context inner-class. 030 * 031 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 032 * @version 1.0.1 033 */ 034 public final class ContextInvocationHandler implements InvocationHandler 035 { 036 //------------------------------------------------------------------- 037 // static 038 //------------------------------------------------------------------- 039 040 /** 041 * Construct a new context instance implementing the supplied class 042 * and backed by entries in the supplied map. 043 * 044 * @param clazz the context inner class 045 * @param map a map of context entry keys to values 046 * @return the proxied context instance 047 */ 048 public static Object getProxiedInstance( Class clazz, Map map ) 049 { 050 ClassLoader classloader = clazz.getClassLoader(); 051 ContextInvocationHandler handler = new ContextInvocationHandler( map ); 052 return Proxy.newProxyInstance( classloader, new Class[]{clazz}, handler ); 053 } 054 055 //------------------------------------------------------------------- 056 // state 057 //------------------------------------------------------------------- 058 059 /** 060 * A map containing key values. 061 */ 062 private final Map m_map; 063 064 //------------------------------------------------------------------- 065 // constructor 066 //------------------------------------------------------------------- 067 068 /** 069 * Create a context invocation handler. 070 * 071 * @param provider the provider 072 */ 073 private ContextInvocationHandler( Map map ) 074 { 075 m_map = map; 076 } 077 078 //------------------------------------------------------------------- 079 // implementation 080 //------------------------------------------------------------------- 081 082 /** 083 * Invoke the specified method on underlying object. 084 * This is called by the proxy object. 085 * 086 * @param proxy the proxy object 087 * @param method the method invoked on proxy object 088 * @param args the arguments supplied to method 089 * @return the return value of method 090 * @throws Throwable if an error occurs 091 */ 092 public Object invoke( final Object proxy, final Method method, final Object[] args ) throws Throwable 093 { 094 Class source = method.getDeclaringClass(); 095 if( Object.class == source ) 096 { 097 return method.invoke( this, args ); 098 } 099 else 100 { 101 String name = method.getName(); 102 if( name.startsWith( "get" ) ) 103 { 104 String key = Introspector.decapitalize( name.substring( 3 ) ); 105 Object value = m_map.get( key ); 106 if( null != value ) 107 { 108 Class clazz = method.getReturnType(); 109 if( isAssignableFrom( clazz, value.getClass() ) ) 110 { 111 return value; 112 } 113 else 114 { 115 Expression expression = new Expression( clazz, "new", new Object[]{value} ); 116 return expression.getValue(); 117 } 118 } 119 else if( ( null != args ) && args.length > 0 ) 120 { 121 return args[0]; 122 } 123 else 124 { 125 final String error = 126 "Unable to resolve a context entry value for the key [" + key + "]."; 127 throw new IllegalStateException( error ); 128 } 129 } 130 throw new UnsupportedOperationException( name ); 131 } 132 } 133 134 private static boolean isAssignableFrom( Class clazz, Class c ) 135 { 136 if( clazz.isPrimitive() ) 137 { 138 if( Integer.TYPE == clazz ) 139 { 140 return Integer.class.isAssignableFrom( c ); 141 } 142 else if( Boolean.TYPE == clazz ) 143 { 144 return Boolean.class.isAssignableFrom( c ); 145 } 146 else if( Byte.TYPE == clazz ) 147 { 148 return Byte.class.isAssignableFrom( c ); 149 } 150 else if( Short.TYPE == clazz ) 151 { 152 return Short.class.isAssignableFrom( c ); 153 } 154 else if( Long.TYPE == clazz ) 155 { 156 return Long.class.isAssignableFrom( c ); 157 } 158 else if( Float.TYPE == clazz ) 159 { 160 return Float.class.isAssignableFrom( c ); 161 } 162 else if( Double.TYPE == clazz ) 163 { 164 return Double.class.isAssignableFrom( c ); 165 } 166 else 167 { 168 final String error = 169 "Primitive type [" 170 + c.getName() 171 + "] not supported."; 172 throw new RuntimeException( error ); 173 } 174 } 175 else 176 { 177 return clazz.isAssignableFrom( c ); 178 } 179 } 180 }